const { baseRefNames } = require("./Definition");
const { writeFileSync, delDir, mkdirSync } = require("./FileHelper");
const { getMergeConfig } = require("./mergeConfig");
const { nameToCaml, findRef, nameToLowerCaseCaml } = require("./utils");

const OnlyEnglishAndNumberExg = /^[A-Za-z][0-9A-Za-z\-\_]+$/gm

function Api(method, path, apiInstance) {
  this.queryParams = [];
  this.bodyParam = null;
  this.refs = [];
  this.path = path;
  this.method = method;
  this.tag = apiInstance.tagsMap[apiInstance.tags[0]] || "";
  this.summary = apiInstance.summary;
  this.consumes = apiInstance.consumes;
  this.operationId = nameToLowerCaseCaml(apiInstance.operationId || this.path.split('/').pop());
  OnlyEnglishAndNumberExg.lastIndex = 0
  const isOnly = OnlyEnglishAndNumberExg.test(this.tag)
  this.className = isOnly ? nameToCaml(this.tag) : (path.split("/")[1] + 'Controller');
  if(this.className === 'userController') {
    console.log(this.tag)
  }
  this.parameters = apiInstance.parameters;
  if(apiInstance.requestBody) {
    this.requestBody = apiInstance.requestBody['content']['application/json']
  }
  this.responses = apiInstance.responses;
  this.basePath = apiInstance.basePath || ""
  this.host = apiInstance.host || ""
  if(this.basePath == "/") this.basePath = ""
  if(this.responses[200].schema) {
    this.refs.push(this.responses[200].schema.$ref)
  }
  if(this.parameters) {
    this.parameters.forEach(param=> {
      if(param.in == "query") {
        this.queryParams.push(param);
      }
      if(param.in == "body" && (param.schema.$ref || param.schema.type === 'array')) {
        this.bodyParam = param;
      }
      if(param.schema && param.schema.$ref) {
        this.refs.push(param.schema.$ref)
      }
    })
  }
  if(this.requestBody) {
    var param = this.requestBody
    if(param.schema.$ref || param.schema.type === 'array') {
      this.bodyParam = param;
    }
    if(param.schema && param.schema.$ref) {
      this.refs.push(param.schema.$ref)
    }
  }
}

Api.prototype.findBaseRef = function(ref) {
  var className = ref.replace('#/definitions/', '')
  if(baseRefNames.includes(className)) {
    if (className.includes('«')) {
      className = className.replace(/«/gi, "<")
    }
    if (className.includes('»')) {
      className = className.replace(/»/gi, ">")
    }
    if (className.includes('<string>')) {
      className = className.replace('<string>', "<String>")
    }
    if (className.includes('<boolean>')) {
      className = className.replace('<boolean>', '<bool>')
    }
    if (className.includes('<object>')) {
      className = className.replace('<object>', '<dynamic>')
    }
    if (className.includes('Result')) {
      className = className.replace('Result', "ResultBase")
    }
    return className
  }
  return null
}

Api.prototype.findRef = function(ref) {
  var _ref = findRef(ref)
  if(_ref && !_ref.hasRef) return null
  return !!_ref ? _ref.data : null
}

Api.prototype.parseResponse = function() {
  if(this.responses[200].schema.$ref) {
    var ref = this.responses[200].schema.$ref;
    if(this.findBaseRef(ref)) {
      return this.findBaseRef(ref)
    }
    if(this.findRef(ref)) {
      var className = this.findRef(ref).className;
      var refClassName = nameToCaml(className);
      if(refClassName == "Result") return "dynamic";
      return refClassName;
    }
  }
  return 'dynamic';
}
Api.prototype.parseResponseTs = function() {
  if(this.responses[200].schema && this.responses[200].schema.$ref) {
    var ref = this.responses[200].schema.$ref;
    if(!this.findRef(ref)) return 'any'
    var className = this.findRef(ref).className;
    var refClassName = nameToCaml(className);
    if(refClassName == "Result") return "any";
    return refClassName;
  } else {
    return 'any';
  }
}

Api.prototype.parseBodyParam = function() {
  if(!this.bodyParam || !(this.bodyParam.schema.$ref || this.bodyParam.schema.type === 'array')) return '';
  if(!this.findRef(this.bodyParam.schema.$ref)) return '';
  var refClassName = nameToCaml(this.findRef(this.bodyParam.schema.$ref).className);
  return `${refClassName} data,`
}

Api.prototype.parseBodyParamTs = function() {
  if(!this.bodyParam || !(this.bodyParam.schema.$ref || this.bodyParam.schema.type === 'array')) return '';
  if(!this.findRef(this.bodyParam.schema.$ref)) return '';
  var refClassName = nameToCaml(this.findRef(this.bodyParam.schema.$ref).className);
  return `data: ${refClassName},`
}

Api.prototype.parseBodyParamJs = function() {
  if(!this.bodyParam || !(this.bodyParam.schema.$ref || this.bodyParam.schema.type === 'array')) return '';
  return `data,`
}

exports.Api = Api;

function genApiControllers() {
  var mergeConfig = getMergeConfig()
  var apis = [];
  var tagsMap = {}
  var tags = mergeConfig.apiDoc.tags || []
  tags.forEach((tag) => {
    if(!tag.name || !tag.description) return
    tagsMap[tag.name] = tag.description.replace(/\s/g, '')
  })

  Object.keys(mergeConfig.apiDoc.paths).forEach((_path)=> {
      Object.keys(mergeConfig.apiDoc.paths[_path]).forEach((method) => {
          var apiInstance = mergeConfig.apiDoc.paths[_path][method];
          apiInstance.basePath = mergeConfig.apiDoc.basePath
          apiInstance.host = mergeConfig.host
          apiInstance.tagsMap = tagsMap
          var api = new Api(method, _path, apiInstance)
          apis.push(api);
      })
  })

  var classList = {}
  apis.forEach((_api)=> {
    if(classList[_api.className] == null)
      classList[_api.className] = [];
    classList[_api.className].push(_api)
  })

  Object.keys(classList).map(key=> {
    var classString = "";
    var methodsString = "";
    var refs = [];
    classList[key].map(item=> {
      refs = refs.concat(item.refs || []);
      methodsString += mergeConfig.methodTmpl(item);
    })
    var arrayRef = Array.from(new Set(refs)).map((ref)=> {
      var $ref = findRef(ref);
      if(!$ref) return "";
      /// 如果这个Ref没有文件 则特殊处理
      if(!$ref.hasRef) {
        /// 如果是Result的基础类型 则返回 definitions/Result
        if(baseRefNames.includes($ref.data.className)) {
          return "definitions/Result";
        }
      }
      return "definitions/" + nameToCaml($ref.data.className || "")
    })

    classString = mergeConfig.classTmpl(
      nameToCaml(key),
      methodsString,
      mergeConfig.shouldGenDefinition ?
      mergeConfig.definitionClassRefsTmpl(Array.from(new Set(arrayRef))) : ""
    );
    if(classString != "") {
      var controllerFilePath = `${mergeConfig.DIR_CONTROLLERS}/${nameToCaml(key)}.${mergeConfig.DEFINITION_FILE_EXT}`;
      writeFileSync(controllerFilePath, classString)
    }
  })
}

module.exports.genApiControllers = genApiControllers
